
define([
    "display",
    "util/gl-util",
    "util",
    "programs",
    "js/bolomap.js",
    "js/meshes/testmesh.js",
    "js/util/gl-matrix.js"
    ], 
    function(displayModule,glUtil,util,programs,bolomap,meshes) {//display, 
        
        var GameObject=function(){
            var go={
                id:objIdBase++,
                matrix:mat4.identity(mat4.create()),
                components:[],
                removeComponent: function(comp){
                    this.components.splice(this.components.indexOf(comp),1);
                    delete this[name];
                },
                addComponent: function(name,comp){
                    this[name]=comp;
                    this.components.push(comp);
                }
            }
            return go;
        }
        
        var objIdBase=0;
        var objects = util.ObjectPool(GameObject);
        var frameRenderer=null;
        var worldMeter=10.0;
        var tileDim = worldMeter*5;
        var tileRad = tileDim/2.0;
        var mapOrigin=[120,130];
        var playerList=[];
        
        var updateGameObject={
            update:function(go){
                if(go.update)
                    go.update();
            }
        }
        
        function update(gl,display,timing){
            if(frameRenderer==null)
                frameRenderer=display.createFrameRenderer(gl,timing);
            
            objects.updateActive(updateGameObject);
            
            objects.updateActive(frameRenderer);
        }
        
        function getCell(x,y){
            x+=mapOrigin[0];
            y+=mapOrigin[1];
            if(x<0)x=0;
            else if(x>255)x=255;
            if(y<0)y=0;
            else if(y>255)y=255;
            return bolomap.map[x+(y*256)];
        }
        
        function getNeighborsOfName(mx,my,name){
            var dbits=0;
            if(getCell(mx,my-1)[0].name==name)dbits|=1;
            if(getCell(mx+1,my)[0].name==name)dbits|=2;
            if(getCell(mx,my+1)[0].name==name)dbits|=4;
            if(getCell(mx-1,my)[0].name==name)dbits|=8;
            return dbits;
        }
        
        function getCellAtWorld(fx,fy){
            var ix=parseInt(Math.floor((fx+tileRad)/tileDim));
            var iy=parseInt(Math.floor((fy+tileRad)/tileDim));
            return getCell(ix,iy);
        }
        
        function getCellRegion(fx,fy,rad,out){
            var minx=parseInt(Math.floor((fx+tileRad-rad)/tileDim));
            var miny=parseInt(Math.floor((fy+tileRad-rad)/tileDim));
            var maxx=parseInt(Math.floor((fx+tileRad+rad)/tileDim));
            var maxy=parseInt(Math.floor((fy+tileRad+rad)/tileDim));
            var i=0;
            for(var y=miny;y<maxy;y++){
                for(var x=minx;x<maxx;x++){
                    out[i++]=getCell(x,y);
                }
            }
            return i;
        }    
    

        function cellPassable(tile){
            if(tile[0].name=="Building")return false;
            return true;
        }
        function cellCoordToWorld(x,y,out){
            out[0]=x*tileDim;
            out[1]=y*tileDim;
            if(x<0)out[0]+=tileRad;
            else if(x>0)out[0]-=tileRad;
        }
        
        var colPVS=new Array(0);
        function getBlockedNeighborPositions(fx,fy,rad,out){
            var minx=parseInt(Math.floor((fx+tileDim-rad)/tileDim));
            var miny=parseInt(Math.floor((fy+tileDim-rad)/tileDim));
            var maxx=parseInt(Math.floor((fx+tileDim+rad)/tileDim));
            var maxy=parseInt(Math.floor((fy+tileDim+rad)/tileDim));
            
            var ncells=(maxy-miny)*(maxx-minx);
            if(out.length<ncells){
                out.length=ncells;//=new Array(ncells);
                for(var i=0;i<ncells;i++)out[i]=[0.0,0.0,0.0];
            }
            i=0;
            for(var y=miny;y<maxy;y++){
                for(var x=minx;x<maxx;x++){
                    var cell=getCell(x,y);
                    if(cellPassable(cell)==false)
                        cellCoordToWorld(x,y,out[i++]);
                }
            }
            return i;
        }
        
        function radiusPassable(fx,fy,radius){
            var nn = getBlockedNeighborPositions(fx,fy,radius,colPVS);
            for(var i=0;i<nn;i++){
                //TODO:check minkowski distance to all neighboring blocked tiles..
                
                return false;
            }
            return true;
        }
    
    
        function randElem(arr){
            return arr[parseInt(Math.random()*arr.length*0.999)];
        }
        
        
        
        
        var gl=null;
        var display=null;
        var tileDiffuse=null;
        var tileShader=null;
        
        function bindToUnit(unit){
            gl.activeTexture(gl.TEXTURE0 + unit);
            gl.bindTexture(gl.TEXTURE_2D, this);
        }
        
        

        
        function initWorld(){
            display = displayModule.getDisplay();
            gl = display.gl;
            var makeMeSick=false;
            if(makeMeSick)
                tileShader=programs.createProgramFromTags(gl,'windVS','windFS');
            else
                tileShader=programs.createProgramFromTags(gl,'TNDVS','TNDFS');
            
            tileDiffuse=glUtil.createSolidTexture(gl,[0,0,0,0]);
            tileDiffuse.bindToUnit=bindToUnit;
            
            var tileTex=glUtil.loadTexture(gl,"tiles.png",function(glTex){
                tileTex.bindToUnit=bindToUnit;
                tileDiffuse=tileTex;
                objects.updateActive({
                    tex:tileTex,
                    update:function(obj){
                        obj.diffuseSampler=tileTex;
                    }
                });
            });
        
        }

        var meshList=[]
        for(var me in meshes)
            meshList.push(me);
        
        var tileData={
            "Building":{mesh:meshes.building,speed:0.0},
            "River":{mesh:meshes.river,speed:0.3},
            "Swamp":{mesh:meshes.swamp,speed:0.5},
            "Crater":{mesh:meshes.grass,speed:0.8},
            "Road":{mesh:meshes.road,speed:1.0},
            "Forest":{mesh:meshes.forest,speed:0.8},
            "Rubble":{mesh:meshes.crater,speed:0.4},
            "Grass":{mesh:meshes.grass,speed:0.8},
            "ShotBuilding":{mesh:meshes.building02,speed:0.0},
            "RiverWithBoat":{mesh:meshes.river,speed:1.0},
            "Ocean":{mesh:meshes.ocean,speed:0.25},
        }

        function buildPatch(mox,moy,patchRad)
        {
            var batch=display.geomBatch();
            for(var x=-patchRad;x<patchRad;x++){
                for(var y=-patchRad;y<patchRad;y++){
                    var mat=mat4.identity(mat4.create());
                    mat4.translate(mat,[x*tileDim,y*tileDim,0.0]);

                    var mx=x+mox;
                    var my=y+moy;
                    var tid=getCell(mx,my)[0].name;
                    var mesh=tileData[tid].mesh;
                    var directional=false;
                    var meshBase="";
                    if(tid=="Road"){
                        // instanceMesh(meshes.crater,mat,batch);
                        directional=true;
                        meshBase="road";
                    }else if(tid=="River"){
                        directional=true;
                        meshBase="river";
                    }else if(tid=="Building"){
                        directional=true;
                        meshBase="building";
                    }
                    var dirLUT={
                        0:["",0],
                        1:["0",2],
                        2:["0",3],
                        4:["0",0],
                        8:["0",1],

                        3:["01",3],
                        6:["01",0],
                        12:["01",1],
                        9:["01",2],

                        5:["02",0],
                        10:["02",1],

                        7:["012",0],
                        14:["012",1],
                        13:["012",2],
                        11:["012",3],

                        15:["0123",0],

                    }
                    if(directional){
                        var dbits=getNeighborsOfName(mx,my,tid);
                        if(tid=="River"){
                            dbits|=getNeighborsOfName(mx,my,"Ocean");
                        }
                        var tdata=dirLUT[dbits];
                        var meshName=meshBase+tdata[0];
                        if(tid=="Building"&&tdata[0]!="02")meshName="building";
                        else if(tid=="River"&&tdata[0]=="0123")meshName="river";
                        else if(tid=="Road"&&tdata[0]=="0123")meshName="road";
                        mesh = meshes[meshName];
                        mat4.rotateZ(mat,tdata[1]*Math.PI*0.5);
                    }else{
                        mat4.rotateZ(mat,parseInt(Math.random()*3.99)*Math.PI*0.5);
                    }
                    var scl=tileRad;
                    mat4.scale(mat,[scl,scl,scl]);
                    display.instanceMesh(mesh,batch,mat);
                }
            }
            return batch;
        }

        //var tileTexture=textures.get(gl,"tiles.png");
        //var skyTexture=textures.get(gl,"tiles.png");

        function buildPatchObject(x,y,patchRad){
            var batch=buildPatch(x,y,patchRad);
            var mesh=display.mesh(gl,
                batch.vertices,
                batch.indices,
                batch.normals,
                batch.uvs);
            var meshRenderer = display.meshRenderer(gl,mesh,tileShader);
            var obj=objects.allocate();
            obj.addComponent('meshRenderer',meshRenderer);
            obj.diffuseSampler=tileDiffuse;
            mat4.identity(obj.matrix);
            //mat4.scale(obj.matrix,[sfrnd(10),sfrnd(10),sfrnd(10)]);
            mat4.translate(obj.matrix,[x*tileDim,y*tileDim,0.0]);
            return obj;
        }
        var tmat=mat4.identity(mat4.create());
        function createSingleMeshRenderer(meshName){
            var batch=display.geomBatch();
            
            mat4.identity(tmat);    
            var scl=tileRad;
            mat4.scale(tmat,[scl,scl,scl]);
            
            display.instanceMesh(meshes[meshName],batch,tmat);
            var mesh=display.mesh(gl,
                batch.vertices,
                batch.indices,
                batch.normals,
                batch.uvs);
            var meshRenderer = display.meshRenderer(gl,mesh,tileShader);
            return meshRenderer; 
        }
        
        function addObject(meshName,position){
            var obj=objects.allocate();
            var batch=display.geomBatch();
            mat4.identity(obj.matrix);    
            var scl=tileRad;
            mat4.scale(obj.matrix,[scl,scl,scl]);
            display.instanceMesh(meshes[meshName],batch,obj.matrix);

            var mesh=display.mesh(gl,
                batch.vertices,
                batch.indices,
                batch.normals,
                batch.uvs);
            var meshRenderer = display.meshRenderer(gl,mesh,tileShader);

            obj.addComponent('meshRenderer',meshRenderer);
            obj.diffuseSampler=tileDiffuse;


            mat4.identity(obj.matrix);
            mat4.translate(obj.matrix,position);
            return obj;
        }
        
        function moveTileObject(obj,fx,fy,fz){
            var cell=getCellAtWorld(fx,fy);
            var ci=cell.indexOf(obj);
            if(ci>=0)return;    //Haven't changed cells
            obj.cell.splice(obj.cell.indexOf(obj),1);
            cell.push(obj);
            obj.cell=cell;
        }
        
        function addTileObject(meshName,x,y){
            x-=mapOrigin[0];
            y-=mapOrigin[1];
            var obj=addObject(meshName,[x*tileDim,y*tileDim,0.0]);
            obj.cell=getCell(x,y);
            obj.cell.push(obj);
            obj.name=meshName;
            return obj;
        }
        
        function rebuildPatchAtCell(x,y){
            
        }
        
        function makeScene(display){

            //	for(var t=0;t<640;t++){
            //		genSquare(sfrnd(10),sfrnd(10),sfrnd(10),sfrnd(Math.PI),sfrnd(Math.PI));
            //	}
            var gl=display.gl;
            var patchRad=8;
            var rgnRad=5;
            
            for(var x=-patchRad;x<patchRad;x++)for(var y=-patchRad;y<patchRad;y++){
                //if((x&1)!=(y&1))
                var patch=buildPatchObject(x*(rgnRad*2),y*(rgnRad*2),rgnRad);//,30,3.14159,3.14159);
                //patch.matrix[14]=x*10.0;
            }
            for(var i in bolomap.bases){
                var base=bolomap.bases[i];
                addTileObject("base",base.x,base.y);
            }
            for(var i in bolomap.pillboxes){
                var pill=bolomap.pillboxes[i];
                addTileObject("turret",pill.x,pill.y);
            }

            for(var i in bolomap.starts){
                var start=bolomap.starts[i];
                //addTileObject("boat",start.x,start.y);
            }
        
            var spawnpt=randElem(bolomap.starts);
            boat = addTileObject("boat",spawnpt.x,spawnpt.y);
            var newPlayer={
                name: "tron",
                avatar: addTileObject("tank",spawnpt.x,spawnpt.y)
            }
            newPlayer.avatar.boat=boat;
            playerList.push(newPlayer);
        }
        
        return{
            makeScene: makeScene,
            update: update,
            players: playerList,
            getCellAtWorld: getCellAtWorld,
            getCellRegion: getCellRegion,
            radiusPassable: radiusPassable,
            cellPassable: cellPassable,
            addTileObject: addTileObject,
            moveTileObject: moveTileObject,
            addObject: addObject,
            initWorld: initWorld,
            worldMeter: worldMeter,
            tileData: tileData,
            tileShader: tileShader,
            createSingleMeshRenderer: createSingleMeshRenderer
        }
    }
    );